package spider

import (
	"asocrawler/conf"
	"asocrawler/db"
	"asocrawler/tool"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"runtime/debug"
	"strings"
	"time"
)

var KeywordUrlMap = map[string]string{
	"keywordTrend": "https://trendinsight.oceanengine.com/api/open/index/get_multi_keyword_hot_trend",
	"relationWord": "https://trendinsight.oceanengine.com/api/open/index/get_relation_word",
	"portrait":     "https://trendinsight.oceanengine.com/api/open/index/get_portrait",
}

type KeywordData struct {
	KeywordTrend KeywordTrend
	RelationWord RelationWord
	Portrait     Portrait
}

type KeywordTrend struct {
	HotList []struct {
		//Keyword string `json:"keyword"`
		HotList []struct {
			Index string `json:"index"` // 综合指数
		} `json:"hot_list"`
		SearchHotList []struct {
			Index string `json:"index"` // 搜索指数
		} `json:"search_hot_list"`
	} `json:"hot_list"`
}

type RelationWord struct {
	//Word             string `json:"word"`
	RelationWordList []struct {
		RelationWord string `json:"relation_word"` //相关词
		//Content_list []struct {
		//	Title string `json:"title"`
		//	Url   string `json:"url"`
		//} `json:"content_list"`
		SearchHot         string `json:"search_hot"`         //
		ScoreRank         string `json:"score_rank"`         //相关词排名
		CorrelationChange bool   `json:"correlation_change"` //趋势
	} `json:"relation_word_list"`
}

type Portrait struct {
	Data []struct {
		NameZh    string `json:"name_zh"`
		LabelList []struct {
			NameZh string  `json:"name_zh"`
			Value  float32 `json:"value"`
			Tgi    float32 `json:"tgi"`
		} `json:"label_list"`
	} `json:"data"`
}

var AppNameList = []string{"aweme", "toutiao"}

type OceanArithmeticSpider struct {
	ChannelName string
}

func (c *OceanArithmeticSpider) println(v ...interface{}) {
	var str = fmt.Sprintf("[%s]", c.ChannelName)
	if conf.Conf.Debug.Switch {
		str += "-[Debug]"
	}
	log.Println(str, fmt.Sprintln(v...))
}

func NewOceanArithmeticSpider() (h *OceanArithmeticSpider) {
	h = &OceanArithmeticSpider{
		ChannelName: "巨量算数",
	}
	return
}

func (this OceanArithmeticSpider) Run() {
	go func() {
		this.run()
	}()
}
func (c *OceanArithmeticSpider) getKeyWordInfoData(keywordList []string, dateDay string) (KeyWordInfo []db.KeyWordInfo, err error) {
	KeywordDataList := make(map[string]KeywordData)

	wd, err := tool.OpenHeadLessWebDriver()
	if err != nil {
		c.println("创建无头浏览器对象失败，err= ", err)
		return nil, err
	}

	defer func() {
		wd.Quit()             // 关闭浏览器
		tool.PutBrowserPool() // 放回池子
	}()

	if err = wd.Get(topiceDeatilUrl); err != nil {
		c.println("打开首页失败!,err=", err)
		return nil, err
	}
	time.Sleep(1 * time.Second)

	for count, keyword := range keywordList {

		var tmp KeywordData
		for k, url := range KeywordUrlMap {

			tryTimes := 0
		re:
			funcScripts := fmt.Sprintf(`window.queryData =  function queryData (data) {
    var p = new Promise(function (resolve, reject) {
        var e = {
            "url": "%s",
            "method": "POST",
            "data": data
        };
        var h = new XMLHttpRequest; h.open(e.method, e.url, true);
        h.setRequestHeader("accept", "application/json, text/plain, */*");
        h.setRequestHeader("content-type", "application/json;charset=UTF-8");
        h.setRequestHeader("tea-uid", "7054893410171930123");
        h.onreadystatechange = function () {
            if (h.readyState != 4) return;
            if (h.readyState === 4 && h.status === 200) {
                resolve(h.response);
            } else {
            }
        };
        h.send(e.data);
    });
    return p;
}`, url)
			_, err = wd.ExecuteScript(funcScripts, nil)
			if err != nil {
				c.println("初始化queryData函数失败，err=", err)
				return nil, err
			}

			parseDate, err := time.Parse("2006-01-02", dateDay)
			if err != nil {
				c.println("collect getKeyWordInfoData error1,err=", err)
				return nil, err
			}
			//只能采集到两天前的数据
			day := parseDate.AddDate(0, 0, -2).Format("20060102")
			params := ""
			switch k {
			case "keywordTrend":
				params = fmt.Sprintf(`'{"keyword_list":["%s"],"start_date":"%s","end_date":"%s","app_name":"aweme","region":[]}'`, keyword, day, day)
				break
			case "relationWord":
				//关联词，只能采集到当前时间的上一周的数据
				s, e := tool.GetLastWeekIntervalTime(dateDay)
				params = fmt.Sprintf(`'{"param":{"keyword": "%s", "start_date": "%s", "end_date": "%s", "app_name": "aweme"}}'`, keyword, strings.ReplaceAll(s, "-", ""), strings.ReplaceAll(e, "-", ""))
				break
			case "portrait":
				params = fmt.Sprintf(`'{"param":{"keyword": "%s", "app_name": "aweme", "start_date": "%s", "end_date": "%s"}}'`, keyword, day, day)
				break
			}
			scripts := fmt.Sprintf(`queryData(%s).then(res =>  {
var callback = arguments[arguments.length - 1];
var pRes = JSON.parse(res)
callback(pRes.data)
})`, params)

			text, err := wd.ExecuteScriptAsync(scripts, nil)

			tres, ok := text.(string)

			if err != nil || !ok {
				if tryTimes < 5 {

					wd.Quit()             // 关闭浏览器
					tool.PutBrowserPool() // 放回池子

					wd, err = tool.OpenHeadLessWebDriver()
					if err != nil {
						c.println("创建无头浏览器对象失败，err= ", err)
						return nil, err
					}

					if err = wd.Get(topiceDeatilUrl); err != nil {
						c.println("打开首页失败!,err=", err)
						return nil, err
					}
					time.Sleep(1 * time.Second)

					c.println("尝试从新请求!", "count:", count, " keyword: ", keyword, "  params: ", params, "   url: ", url, " err=", err)
					tryTimes++
					goto re
				}

				c.println("调用queryData函数失败，err=", err)
				break
			}

			// text是AES-128加密的字符串，需要解密

			jsonStr, err := c.decrypt(tres)
			if err != nil {
				c.println("解密失败，err=", err)
				return nil, err
			}
			switch k {
			case "keywordTrend":
				err = json.Unmarshal([]byte(jsonStr), &tmp.KeywordTrend)
				if err != nil {
					return nil, err
				}
				break
			case "relationWord":
				err = json.Unmarshal([]byte(jsonStr), &tmp.RelationWord)
				if err != nil {
					return nil, err
				}
				break
			case "portrait":
				err = json.Unmarshal([]byte(jsonStr), &tmp.Portrait)
				if err != nil {
					return nil, err
				}
				break
			}

		}
		KeywordDataList[keyword] = tmp
		c.println("采集成功的关键词 ", "count:", count, " keyword: ", keyword)
	}

	//组装数据
	for k, v := range KeywordDataList {
		var tmp db.KeyWordInfo
		tmp.DateDay = dateDay
		tmp.KeyWord = k
		if len(v.KeywordTrend.HotList) > 0 {
			if len(v.KeywordTrend.HotList[0].HotList) > 0 {
				tmp.KeyWordHot = v.KeywordTrend.HotList[0].HotList[0].Index
			}
			if len(v.KeywordTrend.HotList[0].SearchHotList) > 0 {
				tmp.SearchHot = v.KeywordTrend.HotList[0].SearchHotList[0].Index
			}
		}
		//如果两个热度都是0，就是为获取到数据，跳过不存
		if tmp.KeyWordHot == "0" && tmp.SearchHot == "0" {
			c.println("OceanArithmeticSpider getKeyWordInfoData 查询数据为空！关键词：", k)
			continue
		}
		tmp2 := make(map[string][]string)
		for _, v2 := range v.RelationWord.RelationWordList {
			trend := ""
			if v2.CorrelationChange {
				trend = "up"
			} else {
				trend = "down"
			}
			tmp2[v2.RelationWord] = []string{v2.ScoreRank, v2.SearchHot, trend}
		}
		tmp2Str, err := json.Marshal(tmp2)
		if err != nil {
			tmp2Str = []byte("{}")
		}
		tmp.RelationWordList = string(tmp2Str)

		for _, v3 := range v.Portrait.Data {
			t := make(map[string][]float32)
			for _, v4 := range v3.LabelList {
				t[v4.NameZh] = []float32{v4.Value, v4.Tgi}
			}
			jsonStr, err := json.Marshal(t)
			if err != nil {
				c.println(v3.NameZh + "分布信息转JSON失败！")
				jsonStr = []byte("{}")
			}
			if v3.NameZh == "年龄" {
				tmp.AgeDistribution = string(jsonStr)
			} else if v3.NameZh == "性别" {
				tmp.SexDistribution = string(jsonStr)
			} else if v3.NameZh == "地域分布" {
				tmp.ArealDistribution = string(jsonStr)
			} else if v3.NameZh == "城市" {
				tmp.CityDistribution = string(jsonStr)
			}
		}
		KeyWordInfo = append(KeyWordInfo, tmp)
	}
	return
}

func (c *OceanArithmeticSpider) run() {
	defer func() {
		if err := recover(); err != nil {
			errMsg := string(debug.Stack())
			c.println("[崩溃]err=", err, errMsg)
		}
	}()
	if conf.Conf.Debug.Switch {
		for _, fDate := range conf.Conf.Debug.DateList {
			c.println("日期:", fDate, "开始采集数据...")
			succ, err := c.collect(fDate)
			if succ {
				c.println("日期:", fDate, "采集数据完成")
			} else {
				if err == nil {
					c.println("日期:", fDate, "数据未更新")
				} else {
					c.println("日期:", fDate, "出现错误err:", err)
				}
			}
		}
	} else {
	re:
		fDate := time.Now().Format("2006-01-02")

		c.GetDataByDate(fDate)
		time.Sleep(3 * time.Hour)
		goto re
	}

}
func (c *OceanArithmeticSpider) GetDataByDate(date string) {
	c.println("日期:", date, "开始采集数据...")
	success, err := c.collect(date)
	if err != nil {
		tool.SendWarn("ASO报警-巨量算数爬虫报错", "ASO报警-巨量算数爬虫报错,错误信息："+err.Error())
		c.println("日期:", date, "采集失败 err=", err)
	} else {
		if success {
			c.println("日期:", date, "采集数据完成 稍后再次采集...")
		} else {
			c.println("日期:", date, "数据未更新 稍后重新采集...")
		}
	}

}

func (c *OceanArithmeticSpider) collect(dateDay string) (success bool, err error) {
	list, err := db.GetMonitorKeyWordList()
	if err != nil {
		log.Println("GetKeyWordList 获取关键词列表失败! err=", err)
		return
	}

	KeyWordInfoList, err := c.getKeyWordInfoData(list, dateDay)
	if err != nil {
		log.Println("collect getKeyWordData 获取关键词信息失败! err=", err)
		return
	}

	err = db.SaveKayWordInfo(KeyWordInfoList)
	if err != nil {
		log.Println("SaveKayWordInfo faild! err=", err)
		return
	}
	return true, nil
}

func (c *OceanArithmeticSpider) decrypt(t string) (r string, err error) {
	decodeByte, err := base64.StdEncoding.DecodeString(t)
	if err != nil {
		return
	}

	iv := []byte("amlheW91LHFpYW53")
	key := []byte("anN2bXA2NjYsamlh")

	block, err := aes.NewCipher(key)
	if err != nil {
		return
	}

	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(decodeByte, decodeByte)

	r = string(decodeByte)

	return
}
